package main

import (
	"crypto/sha1"
	"encoding/hex"
	"fmt"
	"github.com/djherbis/atime"
	"hash/crc32"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	_net "github.com/shirou/gopsutil/net"
	"gitlab.com/NebulousLabs/go-upnp"
)

var (
	firstFile  string
	firstAtime int64
)

//返回MAC地址
func GetMACAddress() string {
	ip, _ := GetLocalIP()
	mac, _, _ := GetLocalMac(ip)
	return mac
}

//得到本地IP地址
func GetLocalIP() (string, error) {
	netInterfaces, err := net.Interfaces()
	if err != nil {
		return "", err
	}
	for i := 0; i < len(netInterfaces); i++ {
		if (netInterfaces[i].Flags & net.FlagUp) != 0 {
			addrs, _ := netInterfaces[i].Addrs()
			var useIP string
			for _, address := range addrs {
				if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
					if ipnet.IP.To4() != nil {
						useIP = ipnet.IP.String()
					}
				}
			}
			if useIP != "" {
				return useIP, nil
			}
		}
	}
	return "", err
}

//得到本地MAC地址
func GetLocalMac(ip string) (string, string, error) {
	interfaces, err := _net.Interfaces()
	if err != nil {
		return "", "", err
	}
	for _, inter := range interfaces {
		addrs := inter.Addrs
		var macAddress string
		var netName string
		for _, addr := range addrs {
			if strings.Index(addr.Addr, ip) != -1 {
				macAddress = inter.HardwareAddr
				netName = inter.Name
			}
		}
		if macAddress != "" {
			return macAddress, netName, nil
		}
	}
	return "", "", err
}

//设定UPNP
func SetUPnP() {
	nDevices, err := upnp.Discover()
	if err != nil {
		fmt.Println("nDevices: ", err)
		return
	}
	fmt.Println(nDevices, "device(s) detected")

	externalIpAddress, err := nDevices.ExternalIP()
	if err != nil {
		fmt.Println("externalIpAddress: ", err)
	}
	fmt.Println("external ip address :", externalIpAddress)
	r := false
	// r, err := nDevices.IsForwardedTCP(uint16(eport))
	// if err != nil {
	// 	fmt.Println("IsForwardedTCP: ", err)
	// 	// return
	// }
	nDevices.Clear(uint16(eport))

	e := nDevices.Forward(uint16(eport), "PMR UPnP "+strconv.Itoa(eport))
	if e != nil {
		fmt.Println("Unpn Failed")
	} else {
		fmt.Println("Success. Now waiting for some HTTP request on http://%v:%d", externalIpAddress, eport)
	}
	for {
		r, err = nDevices.IsForwardedTCP(uint16(eport))
		if r && eport < 65536 {
			break
		}
	}

}

func ClearCache() {
	fmt.Println("start clear cache")
	start := time.Now()
	defer func() {
		cost := time.Since(start)
		fmt.Println("clear file cost=", cost)
	}()
	DoClear()
}

func DoClear() {
	size, err := DirSize("cache")
	if err != nil {
		panic(err)
	}
	if size > cacheMax {
		delOverflowFile(firstFile)
		DoClear()
	} else {
		fmt.Println("clear finished")
	}
}

func DirSize(path string) (int64, error) {
	firstAtime = time.Now().Unix()
	var size int64
	err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {
		var er error
		if !info.IsDir() {
			filename := info.Name()
			statInfo, err := atime.Stat(filepath.Join("cache/", filename[:2], filename))
			if err == nil {
				infoAtime := statInfo.Unix()
				if infoAtime < firstAtime {
					firstAtime = infoAtime
					firstFile = info.Name()
				}
				size += info.Size()
			}
			er = err
		}
		return er
	})
	return size, err
}

func delOverflowFile(filename string) error {
	fullName := filepath.Join("cache/", filename[:2], filename)
	err := os.Remove(fullName)
	return err
}

//创建缓存目录
func MakeCacheDir() {
	ec := os.Mkdir("cache", os.ModePerm)
	if ec != nil {
		fmt.Println(ec)
	}
	st := os.Mkdir("tmp", os.ModePerm)
	if st != nil {
		fmt.Println(st)
	}
}

//上报当前地址
func Ping(id string) {
	var u url.Values
	u = url.Values{}
	u.Set("id", id)
	u.Set("port", strconv.Itoa(eport))
	u.Set("protocol", "http")
	u.Set("network", networkType)
	//生成要访问的url
	getUrl := "http://47.240.26.163:8989/ping?" + u.Encode()
	request, err := http.NewRequest("GET", getUrl, nil)
	request.Header.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)")
	client := http.Client{Timeout: 5 * time.Second} //创建客户端
	resp, errs := client.Do(request)                //发送请求
	if err != nil || errs != nil {
		fmt.Println("pingError：%v", err, errs)
		return
	}

	defer resp.Body.Close()
	// _, _ := ioutil.ReadAll(resp.Body)
	// fmt.Println("==ping id result is:==", string(body))
}

//SHA1编码
func SHA1(data string) string {
	sha1 := sha1.New()
	sha1.Write([]byte(data))
	return hex.EncodeToString(sha1.Sum(nil))
}

func Uint32ToBytes(n uint32) []byte {
	return []byte{
		byte(n),
		byte(n >> 8),
		byte(n >> 16),
		byte(n >> 24),
	}
}
func GetCRC32(urlstr string) string {
	u, err := url.Parse(urlstr)
	if err != nil {
		panic(err)
	}
	filename := u.Path
	filerealname := strings.TrimSuffix(filename, path.Ext(filename))
	crcname := crc32.ChecksumIEEE([]byte(filerealname))
	return fmt.Sprint(crcname)
}
func GetFileContentType(out string) (string, error) {
	cachefile, err := os.Open(out)
	if err != nil {
		return "", err
	}
	defer cachefile.Close()
	// Only the first 512 bytes are used to sniff the content type.
	buffer := make([]byte, 512)

	_, err = cachefile.Read(buffer)
	if err != nil {
		return "", err
	}

	// Use the net/http package's handy DectectContentType function. Always returns a valid
	// content-type by returning "application/octet-stream" if no others seemed to match.
	contentType := http.DetectContentType(buffer)

	return contentType, nil
}

func Exists(path string) bool {
	_, err := os.Stat(path)
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		return false
	}
	return true
}
